#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <modbus.h>
#include <pthread.h>
#include "cJSON.h"
#include "list.h"
#include "shmem.h"
#include <modbus-tcp.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <unistd.h>
#include "msg_queue_peer.h"
#include <math.h>

#define MAX_NODE 10 //最大支持学生数目
#define STD_NODE_LEN sizeof(struct std_node)
#define MAX_NODE_SIZE (MAX_NODE * STD_NODE_LEN)

static struct shm_param para;
static int *total;                //指向共享内存中数据节点总个数
static struct std_node *node_arr; //指向共享内存中节点缓存数组头

union val_t {
    int b_val;   //bool类型存储空间
    int i_val;   //整形值存储空间
    float f_val; //浮点值存储空间
};

typedef struct std_node //共享内存格式
{
    int key;             //唯一键值
    int type;            //数据点类型
    int dev_type;        //数据点属于哪个设备，根据网关支持的设备自行定义
    union val_t old_val; //变化上报后需要更新旧值
    union val_t new_val; //从共享内存取出最新数据，放到new_val中
    int ret;             //默认为-1，采集成功后设置为0，采集失败再置-1
} node_dev, *p_dev;

typedef struct mb_node //备份链表节点的格式
{
    int key;        //唯一键值
    char name[128]; //数据点名称
    int type;       //数据点类型
    int addr;
} list_node, *plist_node;

struct mb_node_list //备份链表的节点
{
    struct mb_node node;
    struct list_head list;
};

struct msgbuf //消息对列
{
    long mtype;      // modbus
    char mdata[256]; //key，val
};

// struct msgbuf recv_buf; //定义用于接受消息的结构体
struct list_head head;

modbus_t *mod;

float s_TVOC;
float s_JQ;

//创建结构体来接收所用到的key
struct mb_node Key_node;

void Switch(int key, int s); //开关
void Get();                  //读取温度（不用）
void Edit();                 //读取开关状态并 根据开关改变甲醛和co2的含量

uint8_t dest1[32]; //通风开关状态
uint8_t dest2[32]; //空气净化器开关状态

void *Data_get(void *argv) //数据上传到共享内存
{
    uint8_t data1[8] = {0};
    uint8_t dat22[8] = {0};
    //初始化共享内存
    int ret = -1;
    ret = shm_init(&para, "shm_test", MAX_NODE_SIZE);
    if (ret < 0)
    {
        perror("shm_init err\n");
        //return -1;
    }
    void *addr = shm_getaddr(&para); //明一个void指针addr，并将其赋值为shm_getaddr函数的返回值。这个函数返回共享内存段的地址。
    if (addr == NULL)
    {
        perror("shm_getaddr err\n");
    }

    int *num = addr;                                    //声明一个整型指针num，并将其赋值为addr。这个指针用于访问共享内存中存储的整数值。
    total = (int *)addr;                                //将addr的值赋给一个名为total的整型指针。它也用于访问共享内存中存储的整数值。
    node_arr = (struct std_node *)(addr + sizeof(int)); /* 声明了一个struct std_node类型的指针node_arr，并将其赋值为addr加上一个整型的大小。*/

    while (1)
    {
        sleep(2);
        for (int i = 0; i < *num; i++)
        {
            if (node_arr[i].key == 101) //甲醛
            {
                //旧值替换为新值
                node_arr[i].old_val.f_val = node_arr[i].new_val.f_val;
                node_arr[i].new_val.f_val = s_JQ;
                printf("101 = %f", node_arr[i].new_val.f_val);
                node_arr[i].ret = 0;
            }
            else if (node_arr[i].key == 102) //TVOC
            {
                node_arr[i].old_val.f_val = node_arr[i].new_val.f_val;
                node_arr[i].new_val.f_val = s_TVOC;
                 printf("102 = %f\n", node_arr[i].new_val.f_val);
                node_arr[i].ret = 0;
            }
            else if (node_arr[i].key == 103) //通风开关
            {
                node_arr[i].old_val.b_val = node_arr[i].new_val.b_val;
                node_arr[i].new_val.b_val = dest1[0];
                 printf("103=%d 当前状态=%d \n", node_arr[i].new_val.b_val, dest1[0]);
            }
            else if (node_arr[i].key == 104) //空气净化器
            {
                node_arr[i].old_val.b_val = node_arr[i].new_val.b_val;
                node_arr[i].new_val.b_val = dest2[0];
                 printf("104=%d 当前状态=%d \n", node_arr[i].new_val.b_val, dest2[0]);
            }static int *total;    
        }
    }
}
void *Get_t(void *argv) //实时刷新甲醛和TVOC的浓度
{
    Edit();
}

int main(int argc, char const *argv[])
{
    //打开cJSON文件
    FILE *file = fopen("node.json", "r");
    if (file == NULL)
    {
        perror("open err");
        return -1;
    }
    char buf[2048] = {};

    //读取data.cjson文件
    if (fread(buf, 1, 2048, file) < 0)
    {
        perror("read err");
        return -1;
    }

    //把字符串转换成cJSON树
    cJSON *root = cJSON_Parse(buf);
    if (NULL == root)
    {
        perror("parse err\n");
        return -1;
    }

    //获取版本属性树枝
    char *version = cJSON_GetObjectItem(root, "version")->valuestring;
    cJSON *item = cJSON_GetObjectItem(root, "report");
    int type = cJSON_GetObjectItem(item, "type")->valueint;
    int period = cJSON_GetObjectItem(item, "period")->valueint;

    // //打印点表版本
    printf("version:%s\ntype:%d\nperiod:%d\n", version, type, period);

    //获得树干上modbus的子树叉
    item = cJSON_GetObjectItem(root, "mb_dev");
    //获得点表中服务器的地址
    cJSON *IP = cJSON_GetObjectItem(item, "addr");
    //获取点表中的服务器端口号
    cJSON *Port = cJSON_GetObjectItem(item, "port");
    //打印slave设备信息
    printf("addr:%s\nport:%d\n", IP->valuestring, Port->valueint);

    //获得树干上modbus的子树叉
    item = cJSON_GetObjectItem(root, "modbus");
    //从item上得到名为data的子节点
    cJSON *data = cJSON_GetObjectItem(item, "data");

    //获得数组大小
    int array_size = cJSON_GetArraySize(data);
    //创建子链表
    cJSON *JS_data = data->child;

    //插入内核链表
    INIT_LIST_HEAD(&head); //初始化一个名为head的链表头结点
    for (int i = 0; i < array_size; i++)
    {
        //为struct mb_node_list类型的结构体分配内存空间，并将其地址赋值给arr变量。
        struct mb_node_list *arr = (struct mb_node_list *)malloc(sizeof(struct mb_node_list));
        printf("key=%d\n", cJSON_GetObjectItem(JS_data, "key")->valueint);
        printf("name=%s\n", cJSON_GetObjectItem(JS_data, "name")->valuestring);
        printf("addr=%d\n", cJSON_GetObjectItem(JS_data, "addr")->valueint);
        printf("type=%d\n", cJSON_GetObjectItem(JS_data, "type")->valueint);

        //将JSON数据中"key"字段的整数值赋给arr结构体中的node.key成员
        arr->node.key = cJSON_GetObjectItem(JS_data, "key")->valueint;
        arr->node.addr = cJSON_GetObjectItem(JS_data, "addr")->valueint;
        //将arr结构体中的节点添加到head链表中
        list_add(&arr->list, &head);
        //将JS_data指针指向下一个节点，以便在下一次迭代中读取下一个JSON对象的值
        JS_data = JS_data->next;
    }

    //打印内核链表
    struct list_head *pos;    //定义一个类型为struct list_head的指针变量pos，用于表示当前遍历到的节点
    struct mb_node_list *tmp; //定义一个类型为struct mb_node_list的指针变量tmp，用于保存当前遍历到的节点的数据
    list_for_each(pos, &head) /*使用list_for_each宏来遍历链表head中的每个节点，将当前节点的指针保存在pos中 */
    {
        //使用list_entry宏来获取当前节点的数据结构struct mb_node_list的指针，并将其保存在tmp中
        tmp = list_entry(pos, struct mb_node_list, list);
    }
    printf("打印完成\n");

    //以tcp方式创建modbus实例，并初始化
    mod = modbus_new_tcp(IP->valuestring, Port->valueint); //电脑ip
    if (NULL == mod)
    {
        perror("mod err");
        return -1;
    }
    //设置从机ID
    int modslave = modbus_set_slave(mod, 1);
    if (modslave != 0)
    {
        perror("slr");
        return -1;
    }

    //和从机连接
    int modconnect = modbus_connect(mod);
    if (modconnect != 0)
    {
        perror("slave2 err.");
        return -1;
    }

    //创建线程
    pthread_t tid1;
    if (pthread_create(&tid1, NULL, Data_get, NULL))
    {
        perror("create err");
        return -1;
    }
    //创建线程
    pthread_t tid2;
    if (pthread_create(&tid2, NULL, Get_t, NULL))
    {
        perror("create err");
        return -1;
    }

    uint16_t dest[4] = {0};
    struct msgbuf recv_buf;
    while (1)
    {
        /*从名为"modbustopic"的消息队列中接收消息，接收的内容将被存储在recv_buf结构中。
        如果成功接收到消息，并且消息的长度大于0，则执行接下来的操作 */
        if (msg_queue_recv("modbustopic", &recv_buf, sizeof(recv_buf), 2, 0) > 0)
        {
            /*{
	            "type": 2, //指令类型：2-控制指令
	            "data": {
		                    "key": 1, //数据点唯一标识(确保数据点表内的唯一性)
		                    "val": "22.5" //数据点键值，必须用字符串，因为不一定是什么类型的
	                    }
                } */
            cJSON *root = cJSON_Parse(recv_buf.mdata);
            cJSON *item = cJSON_GetObjectItem(root, "data");
            cJSON *item_key = cJSON_GetObjectItem(item, "key");
            cJSON *item_val = cJSON_GetObjectItem(item, "val");

            //使用list_for_each宏遍历链表head`中的每个节点
            list_for_each(pos, &head)
            {
                tmp = list_entry(pos, struct mb_node_list, list);
                if (item_key->valueint == tmp->node.key)
                {
                    if (!strncmp(tmp->node.name, "Exhaust air", strlen(tmp->node.name)))
                    {
                        //将item_val->valuestring转换成int类型
                        Switch(tmp->node.key, atoi(item_val->valuestring));
                    }
                    else if (!strncmp(tmp->node.name, "Air purifier", strlen(tmp->node.name)))
                    {
                        Switch(tmp->node.key, atoi(item_val->valuestring));
                    }
                }
            }
            cJSON_Delete(root);
        }
    }
    //关闭
    Edit(mod);
    // 5.关闭套接字
    modbus_close(mod);
    // 6.释放实例
    modbus_free(mod);
    return 0;
}
void Switch(int key, int s)
{
    uint8_t dest[32];
    if (key == 103) //排风开关 线圈寄存器 1
    {
        modbus_write_bit(mod, 0, s);
    }
    else if (key == 104) //空气净化器开关 线圈寄存器 1
    {
        modbus_write_bit(mod, 1, s);
    }
}
void Get()
{
    while (1)
    {
        sleep(2);
        uint16_t dest[32];
        modbus_read_input_registers(mod, 0, 2, dest);    //0x04 输入寄存器只读
        float temperature = modbus_get_float_dcba(dest); //转成浮点型
    }
}
void Edit() //读取开关状态并 根据开关改变甲醛和co2的含量
{
    while (1)
    {
        sleep(2);
        modbus_read_bits(mod, 0, 1, dest1); //排风开关
        modbus_read_bits(mod, 1, 1, dest2); //空气净化器开关
        if (dest1[0] == 1 && dest2[0] == 1)
        {
            sleep(2);
            //随机值
            int i = rand() % 210;
            s_JQ = i / 10000.00 + 0.01;
            uint16_t dest[2];
            modbus_set_float(s_JQ, dest);
            // printf("甲醛：%f\n", s_JQ);
            modbus_write_registers(mod, 0, 2, dest); //修改甲醛

            i = rand() % 200;
            s_TVOC = i / 10000.00 + 0.005;
            modbus_set_float(s_TVOC, dest);
            // printf("TVOC：%f\n", s_TVOC);
            modbus_write_registers(mod, 2, 2, dest); //修改TVOC
        }
        else if (dest1[0] == 1 || dest2[0] == 1)
        {
            sleep(2);
            int i = rand() % 210;
            s_JQ = i / 10000.00 + 0.03;
            uint16_t dest[2];
            modbus_set_float(s_JQ, dest);
            // printf("甲醛：%f\n", s_JQ);
            modbus_write_registers(mod, 0, 2, dest); //修改甲醛

            i = rand() % 200;
            s_TVOC = i / 10000.00 + 0.01;
            modbus_set_float(s_TVOC, dest);
            // printf("TCOV：%f\n", s_TVOC);
            modbus_write_registers(mod, 2, 2, dest); //修改二氧化碳
        }
        else
        {
            sleep(2);
            int i = rand() % 210;
            s_JQ = i / 10000.00 + 0.05;
            uint16_t dest[2];
            modbus_set_float(s_JQ, dest);
            // printf("甲醛：%f\n", s_JQ);
            modbus_write_registers(mod, 0, 2, dest); //修改甲醛

            i = rand() % 200;
            s_TVOC = i / 10000.00 + 0.02;
            modbus_set_float(s_TVOC, dest);
            // printf("TVOC：%f\n", s_TVOC);
            modbus_write_registers(mod, 2, 2, dest); //修改TVOC
        }
    }
}
